`include "defines.svh"
module BranchPredict (
    input logic clk,
    input logic rst,
    input logic PREIF_IF_Flush,
    input logic PREIF_IF_Stall,
    input logic [31:0]PREIF_PC,
    input FactResult EXE_FactResult,

    output PredictResult IF_PredictResult
);


    logic [31:0]Target;
    logic BranchPredict_Valid;

    RAS_EntryType [`SIZE_OF_RAS-1:0]   RAS;         //RAS块
    logic                              RAS_Wr;      //RAS写有效(决定从call指令调用函数后,由return返回时的下一条指令的地址)
    RAS_EntryType                      RAS_Data;    //旁路后的数据
    logic [$clog2(`SIZE_OF_RAS)-1:0]   RAS_Top;     //RAS栈顶
    logic [$clog2(`SIZE_OF_RAS)-1:0]   RAS_TopSec;  //RAS栈顶下一个

    assign RAS_TopSec = RAS_Top + 3'b111;           //3位,加7即为下一个
    BHT_Entry       BHT_Entry_Writed;
    BHT_Entry       BHT_Entry_Read;
    logic [31:0]    PREIF_PC_Add8;
    logic           BHT_hit;

    assign          PREIF_PC_Add8 = PREIF_PC + 8;

    BPU_RegType     BPU_Reg;

    simple_port_ram_without_bypass #(
                .LATENCY(0),
                .SIZE(`SIZE_OF_SET),
                .dtype(BHT_Entry)
            )mem_data(
                .clk(clk),
                .rst(~rst),
                .ena(1'b1),
                .wea(EXE_FactResult.Valid),
                .addra(EXE_FactResult.PC[`SIZE_OF_INDEX+1:2]),
                .dina(BHT_Entry_Writed),
                .enb(1'b1), 
                .addrb(PREIF_PC[`SIZE_OF_INDEX+1:2]),
                .doutb(BHT_Entry_Read)
            );

//----------------------------对BHT_Entry_Writed进行赋值----------------------//
//即根据分支实际清空更新table
    assign BHT_Entry_Writed.Tag     = EXE_FactResult.PC[31:`SIZE_OF_INDEX+2];
    assign BHT_Entry_Writed.Target  = EXE_FactResult.Target;
    assign BHT_Entry_Writed.Type    = EXE_FactResult.Type;
    assign BHT_Entry_Writed.Valid   = EXE_FactResult.Valid;
//---------------------------------------------------------------------------//
// ----------------------------对饱和计数器进行更新---------------------------//
always_comb begin
    if(EXE_FactResult.Hit == 1'b1)begin                                      //预测命中
        unique case(EXE_FactResult.Count)
        `T:begin
            if(EXE_FactResult.IsTaken)                                      //当前状态为饱和发生
                BHT_Entry_Writed.Count = EXE_FactResult.Count;              //分支发生保持饱和发生
            else 
                BHT_Entry_Writed.Count = EXE_FactResult.Count-1;            //分支不发生降级到不饱和发生(此时仍然预测发生)
        end
        `NT:begin                                                           //当前状态为饱和不发生
            if(EXE_FactResult.IsTaken)                                      //分支发生降级到不饱和不发生(此时仍然预测不发生)
                BHT_Entry_Writed.Count = EXE_FactResult.Count+1;            
            else                                                            //分支不发生保持饱和不发生
                BHT_Entry_Writed.Count = EXE_FactResult.Count;
        end
        default:begin                                                       //当前状态为不饱和(直接进行升降级)
            if(EXE_FactResult.IsTaken)
                BHT_Entry_Writed.Count = EXE_FactResult.Count+1;
            else 
                BHT_Entry_Writed.Count = EXE_FactResult.Count-1;
        end
        endcase
    end 
    else begin                                                              //未命中直接进入不饱和状态
        if(EXE_FactResult.IsTaken)
            BHT_Entry_Writed.Count = `WT;
        else 
            BHT_Entry_Writed.Count = `WNT;
    end 
end
//---------------------------------------------------------------------------//
//-----------------------------对BPU_Reg进行赋值-----------------------------//
always_ff @(posedge clk) begin
    if(rst==1'b0||PREIF_IF_Flush==1'b1)begin
        BPU_Reg.Tag                <= '0;
        BPU_Reg.PCTag              <= '0;
        BPU_Reg.BHT_Addr           <= '0;
        BPU_Reg.RAS_Entry          <= '0;
        BPU_Reg.PC_Add8            <= '0;
        BPU_Reg.Type               <= '0;
        BPU_Reg.Count              <= '0;
        BPU_Reg.Valid              <= '0;
    end
    else if(!PREIF_IF_Stall)begin
        BPU_Reg.Tag                <= BHT_Entry_Read.Tag;
        BPU_Reg.PCTag              <= PREIF_PC[31:`SIZE_OF_INDEX+2];
        BPU_Reg.BHT_Addr           <= BHT_Entry_Read.Target;
        BPU_Reg.RAS_Entry          <= RAS_Data;
        BPU_Reg.PC_Add8            <= PREIF_PC_Add8;
        BPU_Reg.Type               <= BHT_Entry_Read.Type;
        BPU_Reg.Count              <= BHT_Entry_Read.Count;
        BPU_Reg.Valid              <= BHT_Entry_Read.Valid;
    end
end
//---------------------------------------------------------------------------//
    assign BranchPredict_Valid = BPU_Reg.Valid;
    assign BHT_hit = (BPU_Reg.Tag == BPU_Reg.PCTag);                //判断BHT是否命中(地址预测是否命中)
//---------------------------------------------------------------------------//
//--------------------------Target生成逻辑-----------------------------------//
always_comb begin
    if(BHT_hit == 1'b0)begin                                                //未命中时直接选择下一条指令的地址,考虑到延迟槽的存在,即为PC+8
        Target = BPU_Reg.PC_Add8;
        IF_PredictResult.IsTaken = 1'b0;
    end
    else begin
        unique case(BPU_Reg.Type)
            `BranchIsCall: begin                                            //分支为call(调用函数)
                Target = BPU_Reg.BHT_Addr;
                IF_PredictResult.IsTaken = 1'b1;
            end
            `BranchIsReturn: begin                                           //分支为return(函数返回)RAS有效选择RAS记录的返回地址,RAS无效则PC+8(即分支不发生)
                Target = (BPU_Reg.RAS_Entry.Valid)?(BPU_Reg.RAS_Entry.Addr):BPU_Reg.PC_Add8;
                IF_PredictResult.IsTaken = 1'b1;
            end
            `BranchIsBranch: begin                                          //分支为branch类型指令
                if(BPU_Reg.Count[1] == 1'b1) begin                          //根据饱和计数器判断,T|WT预测分支发生
                Target = BPU_Reg.BHT_Addr;
                IF_PredictResult.IsTaken = 1'b1;
                end
                else begin                                                  //NT|WNT预测分支不发生
                Target = BPU_Reg.PC_Add8;
                IF_PredictResult.IsTaken = 1'b0;
                end
            end 
            `BranchIsJump: begin                                            //分支为立即跳转,直接跳
                Target = BPU_Reg.BHT_Addr;
                IF_PredictResult.IsTaken = 1'b1;
            end
            default: begin                                                  //不是分支指令,选择PC+8
                Target = BPU_Reg.PC_Add8;
                IF_PredictResult.IsTaken = 1'b0;
            end
        endcase
    end
end
//---------------------------------------------------------------------------//
//------------------------给IF_PredictResult进行赋值-------------------------//
    assign IF_PredictResult.Type         = (BHT_hit==1'b1)?BPU_Reg.Type:`BranchIsNone;
    assign IF_PredictResult.Target       = Target;
    assign IF_PredictResult.Count        = BPU_Reg.Count;
    assign IF_PredictResult.Hit          = BHT_hit;
    assign IF_PredictResult.Valid        = BranchPredict_Valid;
//---------------------------------------------------------------------------//
//-----------------------------RAS-------------------------------------------//
//更新RAS以及RAS_TOP
always_ff @(posedge clk ) begin
    if(rst == 1'b0) begin
        RAS_Top <= '0;
        RAS     <= '0;
    end
    else if(EXE_FactResult.Valid) begin
        unique case(EXE_FactResult.Type)
        `BranchIsCall: begin                                    //call类型,此时要在RAS中写入返回后需要达到的地址
            RAS[RAS_Top].Valid     <= 1'b1;
            RAS[RAS_Top].Addr      <= EXE_FactResult.PC + 8;
            RAS_Top                <= RAS_Top + 1;
        end
        `BranchIsReturn: begin                                  //return类型,栈指针向下移位
            RAS_Top                <= RAS_TopSec;
        end
        default: begin
            ;
        end
        endcase
    end
end
//---------------------------------------------------------------------------//
//--------------------------RAS的旁路----------------------------------------//
assign RAS_Wr = EXE_FactResult.Valid && (EXE_FactResult.Type == `BranchIsCall);

always_comb begin
    if(RAS_Wr)begin
        RAS_Data.Valid = 1'b1;
        RAS_Data.Addr = EXE_FactResult.PC + 8;
    end
    else begin
        RAS_Data = RAS[RAS_TopSec];
    end
    
end
endmodule